// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LazyValueMetadataCollection.cs" company="AzureContrib (Open source library http://azurecontrib.codeplex.com)">
//   Microsoft Public License (Ms-PL)
// </copyright>
// <summary>
//   Base class for an <see cref="ILazyValueMetadataCollection{TValue,TMetadata,TValueMetadata}" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;

namespace AzureContrib.WindowsAzure.MEF
{
	using AzureContrib.WindowsAzure.ServiceRuntime.ServiceLocation;

	/// <summary>
	/// Base class for an <see cref="ILazyValueMetadataCollection{TValue,TMetadata,TValueMetadata}"/>.
	/// </summary>
	/// <typeparam name="TValue">The type of part.</typeparam>
	/// <typeparam name="TMetadata">The type of metadata described by the part.</typeparam>
	/// <typeparam name="TValueMetadata">The contract which defines the part along with it's metadata.</typeparam>
	public abstract class LazyValueMetadataCollection<TValue, TMetadata, TValueMetadata> : ILazyValueMetadataCollection<TValue, TMetadata, TValueMetadata> where TValueMetadata : TValue, TMetadata
	{
		private Lazy<IBuilder> lazyBuilder;

		/// <summary>
		/// Initializes a new instance of the <see cref="LazyValueMetadataCollection{TValue,TMetadata,TValueMetadata}"/> class. 
		/// Create a new workable instance.
		/// </summary>
		protected LazyValueMetadataCollection()
		{
			ValueMetadatas = Enumerable.Empty<Lazy<TValueMetadata>>();
			ValuesWithMetadata = Enumerable.Empty<Lazy<TValue, TMetadata>>();
		}

		/// <summary>
		/// Gets or sets the current <typeparamref name="TValueMetadata"/> tasks that might be implemented direct in the application.
		/// </summary>
		[ImportMany(AllowRecomposition = true)]
		public IEnumerable<Lazy<TValueMetadata>> ValueMetadatas { get; set; }

		/// <summary>
		/// Gets or sets the current <typeparamref name="TValue" /> implementations in the application that carry <typeparamref name="TMetadata"/>
		/// </summary>
		[ImportMany(AllowRecomposition = true)]
		public IEnumerable<Lazy<TValue, TMetadata>> ValuesWithMetadata { get; set; }

		/// <summary>
		/// Methoid which actually turns a <see cref="Lazy{T,TMetadata}"/> <typeparamref name="TValue"/> plus a <typeparamref name="TMetadata"/> into an instance of <typeparamref name="TValueMetadata"/>.
		/// </summary>
		/// <param name="lazyValueMetadata">
		/// The lazy Value Metadata.
		/// </param>
		/// <returns>
		/// The compact.
		/// </returns>
		public abstract Lazy<TValueMetadata> Compact(Lazy<TValue, TMetadata> lazyValueMetadata);

		/// <summary>
		/// Get an <see cref="IEnumerable{T}.GetEnumerator"/> over all of the <see cref="Lazy{T,TMetadata}"/> <typeparamref name="TValueMetadata"/>s.
		/// </summary>
		/// <returns>
		/// The enumerator.
		/// </returns>
		public IEnumerator<Lazy<TValueMetadata>> GetEnumerator()
		{
			foreach (Lazy<TValue, TMetadata> lazyWorkItem in ValuesWithMetadata)
			{
				Lazy<TValue, TMetadata> copy = lazyWorkItem;
				yield return new Lazy<TValueMetadata>(() =>
																								{
																									// This is all so that the actual BuildUp does not happen until the instance is requested
																									BuildUp(copy.Value);
																									return Compact(copy).Value;
																								});
			}

			foreach (Lazy<TValueMetadata> valueMetadata in ValueMetadatas)
			{
				Lazy<TValueMetadata> copy = valueMetadata;
				yield return new Lazy<TValueMetadata>(() =>
																								{
																									// This is all so that the actual BuildUp does not happen until the instance is requested
																									BuildUp(copy.Value);
																									return copy.Value;
																								});
			}
		}

		/// <summary>
		/// Sets the global <see cref="IBuilder"/> for access to <see cref="IBuilder.BuildUp"/> for instances not created through Inversion of Control.
		/// </summary>
		/// <param name="lazyBuilder">The current global <see cref="IBuilder"/>.</param>
		public void SetBuilder(Lazy<IBuilder> lazyBuilder)
		{
			this.lazyBuilder = lazyBuilder;
		}

		/// <summary>
		/// A standard <see cref="IEnumerable.GetEnumerator"/>.
		/// </summary>
		/// <returns>
		/// The get enumerator.
		/// </returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		private void BuildUp(TValue value)
		{
			if (lazyBuilder != null && lazyBuilder.Value != null)
			{
				lazyBuilder.Value.BuildUp(value);
			}
		}
	}
}